package cn.skynethome.redisx.spring;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import cn.skynethome.redisx.FileUtil;
import cn.skynethome.redisx.RedisMasterSlaveUtil;
import cn.skynethome.redisx.SerializationAndCompressUtils;
import cn.skynethome.redisx.common.ms.JedisClusterPool;
import cn.skynethome.redisx.common.ms.ShardedMasterSlaveJedis;
import cn.skynethome.redisx.common.ms.ShardedMasterSlaveJedisSentinelPool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.util.Pool;

/**
  * 项目名称:[redisx]
  * 包:[cn.skynethome.redisx.spring]    
  * 文件名称:[RedisXSharedSentinel]  
  * 描述:[一句话描述该文件的作用]
  * 创建人:[陆文斌]
  * 创建时间:[2017年1月4日 下午2:47:39]   
  * 修改人:[陆文斌]   
  * 修改时间:[2017年1月4日 下午2:47:39]   
  * 修改备注:[说明本次修改内容]  
  * 版权所有:luwenbin006@163.com
  * 版本:[v1.0]
 */
public class RedisXCluster
{
    protected  Logger logger = Logger.getLogger(RedisXCluster.class);

    private  List<String> hostAndPorts;

    // 可用连接实例的最大数目，默认值为8；
    // 如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
    private  int MAX_ACTIVE = 0;

    // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例，默认值也是8。
    private  int MAX_IDLE = 0;

    // 等待可用连接的最大时间，单位毫秒，默认值为-1，表示永不超时。如果超过等待时间，则直接抛出JedisConnectionException；
    private  int MAX_WAIT = 0;

    // 超时时间
    private  int TIMEOUT = 0;

    private  int maxRedirections;

    // 在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
    private  boolean TEST_ON_BORROW = false;

    private boolean configFlag = false;
    
    private  Pool<JedisCluster> jedisClusterPool = null;

    private  String configPath="properties/redis_cluster.properties";
    
    private String clusterNodes;


    
    
    public String getClusterNodes()
    {
        return clusterNodes;
    }

    public void setClusterNodes(String clusterNodes)
    {
        this.clusterNodes = clusterNodes;
        
        this.hostAndPorts = new LinkedList<String>();

        if (!StringUtils.isEmpty(this.clusterNodes)) {
            String[] address = this.clusterNodes.split(",");
            for (String add : address) {
                this.hostAndPorts.add(add);
            }
        }
    }

    public  int getMAX_ACTIVE()
    {
        return MAX_ACTIVE;
    }

    public  void setMAX_ACTIVE(int mAX_ACTIVE)
    {
        MAX_ACTIVE = mAX_ACTIVE;
    }

    public  int getMAX_IDLE()
    {
        return MAX_IDLE;
    }

    public  void setMAX_IDLE(int mAX_IDLE)
    {
        MAX_IDLE = mAX_IDLE;
    }

    public  int getMAX_WAIT()
    {
        return MAX_WAIT;
    }

    public  void setMAX_WAIT(int mAX_WAIT)
    {
        MAX_WAIT = mAX_WAIT;
    }

    public  int getTIMEOUT()
    {
        return TIMEOUT;
    }

    public  void setTIMEOUT(int tIMEOUT)
    {
        TIMEOUT = tIMEOUT;
    }

    public  int getMaxRedirections()
    {
        return maxRedirections;
    }

    public  void setMaxRedirections(int maxRedirections)
    {
        this.maxRedirections = maxRedirections;
    }

    public  boolean isTEST_ON_BORROW()
    {
        return TEST_ON_BORROW;
    }

    public  void setTEST_ON_BORROW(boolean tEST_ON_BORROW)
    {
        TEST_ON_BORROW = tEST_ON_BORROW;
    }

    public boolean isConfigFlag()
    {
        return configFlag;
    }

    public void setConfigFlag(boolean configFlag)
    {
        this.configFlag = configFlag;
    }

    public  String getConfigPath()
    {
        return configPath;
    }

    public  void setConfigPath(String configPath)
    {
        this.configPath = configPath;
    }

    /**
     * 初始化Redis连接池
     */
    private  void initialPool() {

        initRedisConfigValue();
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(MAX_ACTIVE);
        config.setMaxIdle(MAX_IDLE);
        config.setMaxWaitMillis(MAX_WAIT);
        config.setTestOnBorrow(TEST_ON_BORROW);
        jedisClusterPool = new JedisClusterPool(hostAndPorts, maxRedirections, config, TIMEOUT);

    }

    private  void initRedisConfigValue() {

        // TODO Auto-generated method stub
        // master_server=192.168.1.16
        // master_port=6380
        this.setClusterNodes(FileUtil.getPropertyValue(configPath, "clusterNodes"));

        maxRedirections = FileUtil.getPropertyValueInt(configPath, "maxRedirections");

        MAX_ACTIVE = FileUtil.getPropertyValueInt(configPath, "max_active");

        MAX_IDLE = FileUtil.getPropertyValueInt(configPath, "max_idle");

        MAX_WAIT = FileUtil.getPropertyValueInt(configPath, "max_wait");

        TIMEOUT = FileUtil.getPropertyValueInt(configPath, "timeout");

        TEST_ON_BORROW = FileUtil.getPropertyValueBoolean(configPath, "test_on_borrow");

        FileUtil.remove(configPath);
    }

    /**
     * 在多线程环境同步初始化
     */
    private  synchronized void poolInit() {
        if (jedisClusterPool == null) {
            initialPool();
        }
    }

    /**
     * 同步获取Jedis实例
     * 
     * @return Jedis
     */
    private synchronized  JedisCluster getJedisCluster() {
        if (jedisClusterPool == null) {
            poolInit();
        }
        JedisCluster jedis = null;
        try {
            if (jedisClusterPool != null) {
                jedis = jedisClusterPool.getResource();
            }
        } catch (Exception e) {
            returnResource(jedis);
            logger.error("Get jedis error : " + e);
        }

        return jedis;
    }

    /**
     * 释放jedis资源
     * 
     * @param jedis
     */
    public  void returnResource(final JedisCluster jedis) {
        if (jedis != null && null != jedisClusterPool) {
            jedisClusterPool.returnResource(jedis);
        }
    }

    /**
     * 设置 String
     * 
     * @param key
     * @param value
     */
    public  String setString(String key, String value) {
        JedisCluster jedisCluster = null;
        String r_str = "nil";
        try {
            value = StringUtils.isEmpty(value) ? "" : value;
            jedisCluster = getJedisCluster();
            r_str = jedisCluster.set(key, value);
        } catch (Exception e) {
            logger.error("Set key error : " + e);
        } finally {
            returnResource(jedisCluster);
        }

        return r_str;
    }

    /**
     * 设置 过期时间
     * 
     * @param key
     * @param seconds
     *            以秒为单位
     * @param value
     */
    public  String setString(String key, String value, int expirationTime) {
        JedisCluster jedisCluster = null;
        String r_str = "nil";
        try {
            value = StringUtils.isEmpty(value) ? "" : value;
            jedisCluster = getJedisCluster();
            r_str = jedisCluster.setex(key, expirationTime, value);
        } catch (Exception e) {
            logger.error("Set keyex error : " + e);
        } finally {
            returnResource(jedisCluster);
        }

        return r_str;
    }

    public  Set<byte[]> getKeys(byte[] keys) {
        JedisCluster jedisCluster = getJedisCluster();
        Set<byte[]> set_ = jedisCluster.hkeys(keys);
        returnResource(jedisCluster);
        return set_;
    }

    public  Set<String> getKeys(String keys) {
        JedisCluster jedisCluster = getJedisCluster();
        Set<String> set_ = jedisCluster.hkeys(keys);
        returnResource(jedisCluster);
        return set_;
    }

    public  Set<byte[]> hkeys(byte[] hkeys) {
        JedisCluster jedisCluster = getJedisCluster();
        Set<byte[]> set_ = jedisCluster.hkeys(hkeys);
        returnResource(jedisCluster);
        return set_;
    }

    public  Set<String> hkeys(String hkeys) {
        JedisCluster jedisCluster = getJedisCluster();
        Set<String> set_ = jedisCluster.hkeys(hkeys);
        returnResource(jedisCluster);
        return set_;

    }

    public  Collection<byte[]> hvals(byte[] hvals) {
        JedisCluster jedisCluster = getJedisCluster();
        Collection<byte[]> collection = jedisCluster.hvals(hvals);
        returnResource(jedisCluster);
        return collection;
    }

    public  List<String> hvals(String hvals) {
        JedisCluster jedisCluster = getJedisCluster();
        List<String> list = jedisCluster.hvals(hvals);
        returnResource(jedisCluster);
        return list;
    }

    /**
     * 获取String值
     * 
     * @param key
     * @return value
     */
    public  String getString(String key) {
        JedisCluster jedisCluster = getJedisCluster();
        String r_str = null;
        if (jedisCluster == null || !jedisCluster.exists(key)) {
            r_str = null;
            if (null != jedisCluster) {
                returnResource(jedisCluster);
            }
        } else {
            r_str = jedisCluster.get(key);
        }
        returnResource(jedisCluster);
        return r_str;
    }

    public  String setObject(String key, Object obj, int expirationTime) {
        String r_str = null;

        if (null == obj) {
            r_str = "nil";
        } else {
            JedisCluster jedisCluster = null;
            try {
                jedisCluster = getJedisCluster();
                r_str = jedisCluster.setex(key.getBytes(), expirationTime,
                        SerializationAndCompressUtils.fastSerialize(obj));

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }

        return r_str;

    }

    public  String setObject(String key, Object obj) {
        String r_str = null;

        if (null == obj) {
            r_str = "nil";
        } else {
            JedisCluster jedisCluster = null;
            try {

                jedisCluster = getJedisCluster();
                r_str = jedisCluster.set(key.getBytes(), SerializationAndCompressUtils.fastSerialize(obj));

            } catch (Exception e) {
                // return jedis object to pll and set jedis is null.
                returnResource(jedisCluster);
                jedisCluster = null;
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }

        return r_str;

    }

    public  Long del(String key) {
        Long r_l = 0l;
        JedisCluster jedisCluster = null;
        try {
            jedisCluster = getJedisCluster();
            r_l = jedisCluster.del(key);

        } catch (Exception e) {
            // return jedis object to pll and set jedis is null.
            returnResource(jedisCluster);
            jedisCluster = null;
            e.printStackTrace();
        } finally {

            returnResource(jedisCluster);

        }
        return r_l;

    }

    public  Long del(byte[] key) {
        Long r_l = 0l;
        JedisCluster jedisCluster = null;
        try {
            jedisCluster = getJedisCluster();
            r_l = jedisCluster.del(key);

        } catch (Exception e) {
            // return jedis object to pll and set jedis is null.
            returnResource(jedisCluster);
            jedisCluster = null;
            e.printStackTrace();
        } finally {

            returnResource(jedisCluster);

        }
        return r_l;

    }

    public  boolean exists(byte[] key)
    {
        JedisCluster jedisCluster = getJedisCluster();
        boolean isExists = false;
        
        if (jedisCluster == null)
        {
            isExists = false;
        }
        else
        {
            isExists = jedisCluster.exists(key);
            returnResource(jedisCluster);
        }
        
        return isExists;
    }
    
    public  boolean exists(String key)
    {
        if( null==key )
        {
            return false;
        }
        
        return exists(key.getBytes());
    }
    
    /**
     * 获取Object
     * 
     * @param key
     * @return
     * @return value
     */
    public  <T> T getObject(String key, Class<T> classz) {
        JedisCluster jedisCluster = getJedisCluster();

        T t = null;

        if (jedisCluster == null || !jedisCluster.exists(key.getBytes())) {
            t = null;
            if (null != jedisCluster) {
                returnResource(jedisCluster);
            }
        } else {
            byte[] value = jedisCluster.get(key.getBytes());
            try {

                if (null == value) {
                    t = null;
                } else {
                    t = classz.cast(SerializationAndCompressUtils.fastDeserialize(value));
                }
            } catch (Exception e) {
                // return jedis object to pll and set jedis is null.
                returnResource(jedisCluster);
                jedisCluster = null;
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }

        return t;
    }

    public  Long hdel(String key, String field) {
        JedisCluster jedisCluster = getJedisCluster();
        Long r_l = 0l;
        if (jedisCluster == null) {
            r_l = 0l;
        } else {
            try {
                r_l = jedisCluster.hdel(key, field);
            } catch (Exception e) {
                // return jedis object to pll and set jedis is null.
                returnResource(jedisCluster);
                jedisCluster = null;
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }
        return r_l;
    }

    public  Map<String, String> hgetall(String key) {
        JedisCluster jedisCluster = getJedisCluster();
        Map<String, String> map = null;
        if (jedisCluster == null) {
            map = null;
        } else {
            try {

                map = jedisCluster.hgetAll(key);

            } catch (Exception e) {
                // return jedis object to pll and set jedis is null.
                returnResource(jedisCluster);
                jedisCluster = null;
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }

        return map;
    }

    public  Long hincrBy(String key, String field, long value) {
        JedisCluster jedisCluster = getJedisCluster();
        Long r_l = 0l;
        if (jedisCluster == null) {
            r_l = 0l;
        } else {
            try {
                r_l = jedisCluster.hincrBy(key, field, value);
            } catch (Exception e) {
                // return jedis object to pll and set jedis is null.
                returnResource(jedisCluster);
                jedisCluster = null;
                e.printStackTrace();
            } finally {

                returnResource(jedisCluster);

            }
        }

        return r_l;
    }
    
    public Long zadd(String key, double score, String member)
    {
        JedisCluster jedisCluster = getJedisCluster();
        Long r_l = 0l;
        if (jedisCluster == null)
        {
            r_l = 0l;
        }
        else
        {
            r_l = jedisCluster.zadd(key, score, member);
            returnResource(jedisCluster);
        }

        return r_l;
    }

    public Set<String> zrange(String key, long start, long end)
    {

        Set<String> set = null;
        JedisCluster jedisCluster = getJedisCluster();
        if (jedisCluster == null)
        {
            set = null;

        }
        else
        {
            set = jedisCluster.zrange(key, start, end);
            returnResource(jedisCluster);
        }

        return set;
    }

    public Double zscore(String key, String member)
    {

        Double d_ = 0d;
        JedisCluster jedisCluster = getJedisCluster();
        if (jedisCluster == null)
        {
            d_ = 0d;

        }
        else
        {
            d_ = jedisCluster.zscore(key, member);
            returnResource(jedisCluster);
        }

        return d_;
    }

}
